<?php

/**
 * contact : 刘经理 19326416371
 * company : 版权所属 四川北星惯牌科技有限公司
 **/

namespace app\adminapi\library;

use app\admin\model\Admin;
use app\admin\model\AuthGroup;
use app\admin\model\AuthRule;
use app\common\library\TokenAdmin;
use app\common\model\User;
use fast\Random;
use fast\Tree;
use think\Db;
use think\Exception;
use think\Request;
use think\Validate;

class Auth extends \fast\Auth
{
    protected $breadcrumb = [];
    protected static $instance = null;
    protected $_error = '';
    protected $_logined = false;
    protected $_admin = null;
    protected $_token = '';
    //Token默认有效时长
    protected $keeptime = 2592000;
    protected $requestUri = '';
    protected $rules = [];
    //默认配置
    protected $config = [];
    protected $options = [];
    protected $admin_id = 0;
    protected $superAdmin = false;
    protected $allowFields = ['username','nickname','avatar', 'mobile', 'email'];

    public function __construct()
    {
        parent::__construct();
    }

    /**
     *
     * @param array $options 参数
     * @return Auth
     */
    public static function instance($options = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($options);
        }

        return self::$instance;
    }

    /**
     * 获取User模型
     * @return User
     */
    public function getAdmin()
    {
        return $this->_admin;
    }

    public function getAdminId()
    {
        return $this->admin_id;
    }
    /**
     * 兼容调用user模型的属性
     *
     * @param string $name
     * @return mixed
     */
    public function __get($name)
    {
        return $this->_admin ? $this->_admin->$name : null;
    }

    /**
     * 兼容调用user模型的属性
     */
    public function __isset($name)
    {
        return isset($this->_admin) ? isset($this->_admin->$name) : false;
    }

    /**
     * 根据Token初始化
     *
     * @param string $token Token
     * @return boolean
     */
    public function init($token)
    {
        if ($this->_logined) {
            return true;
        }
        if ($this->_error) {
            return false;
        }
        $data = TokenAdmin::get($token);
        if (!$data) {
            return false;
        }
        $admin_id = intval($data['admin_id']);
        if ($admin_id > 0) {
            $admin = Admin::get($admin_id);
            if (!$admin) {
                $this->setError('账号不存在');
                return false;
            }
            if ($admin['status'] != 'normal') {
                $this->setError('账号异常，无法登陆');
                return false;
            }

            $this->_admin = $admin;
            $this->_logined = true;
            $this->_token = $token;
            $this->admin_id = $admin->id;
            $this->superAdmin = $this->isSuperAdmin();
            return true;
        } else {
            $this->setError('当前未登陆');
            return false;
        }
    }

    /**
     * 用户登录
     *
     * @param string $account  账号
     * @param string $password 密码
     * @return boolean
     */
    public function login($account, $password)
    {
        if(Validate::is($account, 'alphaNum') ){
            $field = 'username';
        }else{
            $this->setError('账号格式有误，请正确输入');
        }
        $admin = Admin::get([$field => $account]);
        if (!$admin) {
            $this->setError('账号不存在');
            return false;
        }

        if ($admin->status != 'normal') {
            $this->setError('账号异常');
            return false;
        }
        if ($admin->password != $this->getEncryptPassword($password, $admin->salt)) {
            $admin->loginfailure++;
            $admin->save();
            $this->setError('密码错误');
            return false;
        }

        TokenAdmin::clear($admin->id);
        //直接登录会员
        return $this->direct($admin->id);
    }

    /**
     * 退出
     *
     * @return boolean
     */
    public function logout()
    {
        if (!$this->_logined) {
            $this->setError('当前未登陆');
            return false;
        }
        //设置登录标识
        $this->_logined = false;
        //删除Token
        TokenAdmin::delete($this->_token);
        //退出成功的事件
        return true;
    }

    /**
     * 修改密码
     * @param string $newpassword       新密码
     * @param string $oldpassword       旧密码
     * @param bool   $ignoreoldpassword 忽略旧密码
     * @return boolean
     */
    public function changepwd($newpassword, $oldpassword = '', $ignoreoldpassword = false)
    {
        if (!$this->_logined) {
            $this->setError('You are not logged in');
            return false;
        }
        //判断旧密码是否正确
        if ($this->_admin->password == $this->getEncryptPassword($oldpassword, $this->_admin->salt) || $ignoreoldpassword) {
            Db::startTrans();
            try {
                $salt = Random::alnum();
                $newpassword = $this->getEncryptPassword($newpassword, $salt);
                $this->_admin->save(['loginfailure' => 0, 'password' => $newpassword, 'salt' => $salt]);

                TokenAdmin::delete($this->_token);
                //修改密码成功的事件
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage());
                return false;
            }
            return true;
        } else {
            $this->setError('Password is incorrect');
            return false;
        }
    }

    /**
     * 直接登录账号
     * @param int $adminId
     * @return boolean
     */
    public function direct($adminId)
    {
        $admin = Admin::get($adminId);
        if ($admin && $admin->status == Admin::STATUS_NORMAL) {
            Db::startTrans();
            try {
                $ip = request()->ip();
                $time = time();
                //记录本次登录的IP和时间
                $admin->loginip = $ip;
                $admin->logintime = $time;
                //重置登录失败次数
                $admin->loginfailure = 0;

                $admin->save();

                $this->_admin = $admin;

                $this->_token = Random::uuid();
                TokenAdmin::set($this->_token, $admin->id, $this->keeptime);

                $this->_logined = true;
                $this->admin_id = $admin->id;
                $this->superAdmin = $this->isSuperAdmin();
                Db::commit();
            } catch (Exception $e) {
                Db::rollback();
                $this->setError($e->getMessage().$e->getLine());
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 判断是否登录
     * @return boolean
     */
    public function isLogin()
    {
        if ($this->_logined) {
            return true;
        }
        return false;
    }

    /**
     * 获取当前Token
     * @return string
     */
    public function getToken()
    {
        return $this->_token;
    }

    /**
     * 获取会员基本信息
     */
    public function getAdminInfo()
    {
        $data = $this->_admin->toArray();
        $allowFields = $this->getAllowFields();
        $adminInfo = array_intersect_key($data, array_flip($allowFields));
        $adminInfo = array_merge($adminInfo, TokenAdmin::get($this->_token));
        $adminInfo['superAdmin'] = $this->isSuperAdmin();
        return $adminInfo;
    }

    /**
     * 获取当前请求的URI
     * @return string
     */
    public function getRequestUri()
    {
        return $this->requestUri;
    }

    /**
     * 设置当前请求的URI
     * @param string $uri
     */
    public function setRequestUri($uri)
    {
        $this->requestUri = $uri;
    }

    /**
     * 获取允许输出的字段
     * @return array
     */
    public function getAllowFields()
    {
        return $this->allowFields;
    }

    /**
     * 设置允许输出的字段
     * @param array $fields
     */
    public function setAllowFields($fields)
    {
        $this->allowFields = $fields;
    }

    /**
     * 删除一个指定管理员
     * @param int $admin_id 管理员
     * @return boolean
     */
    public function delete($admin_id)
    {
        $admin = Admin::get($admin_id);
        if (!$admin) {
            return false;
        }
        Db::startTrans();
        try {
            // 删除会员
            Admin::destroy($admin_id);
            // 删除会员指定的所有Token
            TokenAdmin::clear($admin_id);
            Db::commit();
        } catch (Exception $e) {
            Db::rollback();
            $this->setError($e->getMessage());
            return false;
        }
        return true;
    }

    /**
     * 获取密码加密后的字符串
     * @param string $password 密码
     * @param string $salt     密码盐
     * @return string
     */
    public function getEncryptPassword($password, $salt = '')
    {
        return md5(md5($password) . $salt);
    }


    public function check($name, $uid = '', $relation = 'or', $mode = 'url')
    {
        $uid = $uid ? $uid : $this->id;
        return parent::check($name, $uid, $relation, $mode);
    }
    /**
     * 检测当前控制器和方法是否匹配传递的数组
     *
     * @param array $arr 需要验证权限的数组
     * @return boolean
     */
    public function match($arr = [])
    {
        $request = Request::instance();
        $arr = is_array($arr) ? $arr : explode(',', $arr);
        if (!$arr) {
            return false;
        }
        $arr = array_map('strtolower', $arr);
        // 是否存在
        if (in_array(strtolower($request->action()), $arr) || in_array('*', $arr)) {
            return true;
        }

        // 没找到匹配
        return false;
    }

    /**
     * 设置会话有效时间
     * @param int $keeptime 默认为永久
     */
    public function keeptime($keeptime = 0)
    {
        $this->keeptime = $keeptime;
    }

    /**
     * 设置错误信息
     *
     * @param string $error 错误信息
     * @return Auth
     */
    public function setError($error)
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError()
    {
        return $this->_error ? __($this->_error) : '';
    }

    public function getRuleIds($adminId = null)
    {
        $adminId = is_null($adminId) ? $this->admin_id : $adminId;
        return parent::getRuleIds($adminId);
    }


    public function isSuperAdmin()
    {
        return in_array('*', $this->getRuleIds()) ? true : false;
    }

    public function getRuleList($adminId):array
    {
        return parent::getRuleList($adminId);
    }

    /**
     * 获得面包屑导航
     * @param string $path
     * @return array
     */
    public function getBreadCrumb($path = '')
    {
        if ($this->breadcrumb || !$path) {
            return $this->breadcrumb;
        }
        $titleArr = [];
        $menuArr = [];
        $urlArr = explode('/', $path);
        foreach ($urlArr as $index => $item) {
            $pathArr[implode('/', array_slice($urlArr, 0, $index + 1))] = $index;
        }
        if (!$this->rules && $this->getAdminId()) {
            $this->getRuleList($this->getAdminId());
        }
        foreach ($this->rules as $rule) {
            if (isset($pathArr[$rule['name']])) {
                $rule['title'] = __($rule['title']);
                $rule['url'] = url($rule['name']);
                $titleArr[$pathArr[$rule['name']]] = $rule['title'];
                $menuArr[$pathArr[$rule['name']]] = $rule;
            }
        }
        ksort($menuArr);
        $this->breadcrumb = $menuArr;
        return $this->breadcrumb;
    }


    public function getChildrenAdminIds($withself = false)
    {
        $childrenAdminIds = [];
        if (!$this->isSuperAdmin()) {
            $groupIds = $this->getChildrenGroupIds(false);
            $authGroupList = \app\admin\model\AuthGroupAccess::field('uid,group_id')
                ->where('group_id', 'in', $groupIds)
                ->select();
            foreach ($authGroupList as $k => $v) {
                $childrenAdminIds[] = $v['uid'];
            }
        } else {
            //超级管理员拥有所有人的权限
            $childrenAdminIds = Admin::column('id');
        }
        if ($withself) {
            if (!in_array($this->id, $childrenAdminIds)) {
                $childrenAdminIds[] = $this->id;
            }
        } else {
            $childrenAdminIds = array_diff($childrenAdminIds, [$this->id]);
        }
        return $childrenAdminIds;
    }

    /**
     * 取出当前管理员所拥有权限的分组
     * @param boolean $withself 是否包含当前所在的分组
     * @return array
     */
    public function getChildrenGroupIds($withself = false)
    {
        //取出当前管理员所有的分组
        $groups = $this->getGroups($this->admin_id);
        $groupIds = [];
        foreach ($groups as $k => $v) {
            $groupIds[] = $v['id'];
        }
        $originGroupIds = $groupIds;
        foreach ($groups as $k => $v) {
            if (in_array($v['pid'], $originGroupIds)) {
                $groupIds = array_diff($groupIds, [$v['id']]);
                unset($groups[$k]);
            }
        }
        // 取出所有分组
        $groupList = \app\admin\model\AuthGroup::where($this->isSuperAdmin() ? '1=1' : ['status' => 'normal'])->select();
        $objList = [];
        foreach ($groups as $k => $v) {
            if ($v['rules'] === '*') {
                $objList = $groupList;
                break;
            }
            // 取出包含自己的所有子节点
            $childrenList = Tree::instance()->init($groupList, 'pid')->getChildren($v['id'], true);
            $obj = Tree::instance()->init($childrenList, 'pid')->getTreeArray($v['pid']);
            $objList = array_merge($objList, Tree::instance()->getTreeList($obj));
        }
        $childrenGroupIds = [];
        foreach ($objList as $k => $v) {
            $childrenGroupIds[] = $v['id'];
        }
        if (!$withself) {
            $childrenGroupIds = array_diff($childrenGroupIds, $groupIds);
        }
        return $childrenGroupIds;
    }
    public function getMenu():array
    {
        $rule = [];
        try {
            $ids = $this->getRuleIds();
            $rule = $this->getMenuList($ids);
        }catch (Exception $exception){
            return [];
        }
        return $rule;
    }

    public function getMenuList($ids):array
    {
        $rules = [];
        try {
            if (in_array('*',$ids)){
                $rules = AuthRule::where('pid',0)->where('name','not in',['addon','category','command','epay','third','wechat'])->order('weigh desc, id asc')->field('id,type,pid,name,title,icon,url,ismenu,menutype,weigh,router,front_icon')->select();
                if ($rules){
                    $rules = collection($rules)->toArray();
                    foreach ($rules as &$rule){
                        $rule['data'] = $this->lower($rule['id']);
                    }
                }
            }else{
                $rules = AuthRule::where('pid',0)->where('name','not in',['addon','category','command','epay','third','wechat'])->where('id','in',$ids)->order('weigh desc, id asc')->field('id,type,pid,name,title,icon,url,ismenu,menutype,weigh,router,front_icon')->select();
                if ($rules){
                    $rules = collection($rules)->toArray();
                    foreach ($rules as &$rule){
                        $rule['data'] = $this->lower($rule['id']);
                    }
                }
            }
        }catch (Exception $exception){
            return [];
        }
        return $rules;
    }

    public function lower($pid,$ids='')
    {
        try {
            $ids = empty($ids) ? $this->getRuleIds($this->admin_id) : $ids;
            if (in_array('*',$ids)){
                $key = array_search('*',$ids);
                if ($key !== false){
                    unset($ids[$key]);
                }
            }
            $lower = AuthRule::where('pid',$pid)->where(function ($query) use($ids){
                if ($ids){
                    $query->whereIn('id',$ids);
                }
            })->order('weigh desc, id asc')->field('id,type,pid,name,title,icon,url,ismenu,menutype,weigh,router,front_icon')->select();
            $lower = collection($lower)->toArray();
            if ($lower){
                $lower = array_map(function ($item){
                    $lowerData = $this->lower($item['id']);
                    if ($lowerData){
                        $data = array_values($lowerData);
                        $item['data'] = $data;
                    }
                    return $item;
                },$lower);
            }
        }catch (Exception $exception){
            return [];
        }

        return array_values($lower);
    }

    public function getGroupList():array
    {
        $rules = [];
        try {
            if ($this->isSuperAdmin()){
                $rules = AuthGroup::where('pid',0)->order('id asc')->field('id,pid,name')->select();
                if ($rules){
                    $rules = collection($rules)->toArray();
                    foreach ($rules as &$rule){
                        $rule['data'] = $this->GroupLower($rule['id']);
                    }
                }
            }else{
                $groups = $this->getGroups($this->getAdminId());
                if ($groups){
                    $ids = array_map(function ($item){
                        return $item['id'];
                    },$groups);
                }

                if(!$ids){
                    return [];
                }

                $rules = AuthGroup::where('id','in',$ids)->order('id asc')->field('id,pid,name')->select();
                if ($rules){
                    $rules = collection($rules)->toArray();
                    foreach ($rules as &$rule){
                        $return = $this->GroupLower($rule['id']);
                        if ($return){
                            $rule['data'] = $return;
                        }
                    }
                }
            }
        }catch (Exception $exception){
            return [];
        }
        return $rules;
    }

    public function GroupLower($pid,$ids='')
    {
        try {
            $GroupLower = AuthGroup::where('pid',$pid)->order('id asc')->field('id,pid,name')->where('status','normal')->select();
            $GroupLower = collection($GroupLower)->toArray();
            if ($GroupLower){
                $GroupLower = array_map(function ($item){
                    $lowerData = $this->GroupLower($item['id']);
                    if ($lowerData){
                        $data = array_values($lowerData);
                        if ($data)
                            $item['data'] = $data;

                    }
                    return $item;
                },$GroupLower);
            }
        }catch (Exception $exception){
            return [];
        }

        return array_values($GroupLower);
    }
}
